package profile

import (
	"context"
	blobpb "coolcar/proto/blob_out/v1"
	profilepb "coolcar/proto/profile_out/v1"
	"coolcar/srvs/rental_srv/dao/profile"
	"time"

	"go.mongodb.org/mongo-driver/mongo"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	"go.uber.org/zap"

	"google.golang.org/protobuf/types/known/emptypb"
)

type Service struct {
	BlobClient  blobpb.BlobServiceClient
	PhotoExpire time.Duration
	Mongo       *profile.Mongo
	Logger      *zap.Logger
	profilepb.UnimplementedProfileServiceServer
}

func (s Service) GetProfile(c context.Context, empty *emptypb.Empty) (*profilepb.Proflile, error) {
	p, err := s.Mongo.GetProfile(c, "account1")
	if err != nil {
		if err == mongo.ErrNoDocuments {
			return &profilepb.Proflile{}, nil
		} else {
			s.Logger.Error("can not get profile", zap.Error(err))
			return nil, status.Error(codes.Internal, "")
		}
	}

	return p.Profile, nil
}

func (s Service) SubmitProfile(c context.Context, identity *profilepb.Identity) (*profilepb.Proflile, error) {
	p := profilepb.Proflile{
		Identity:       identity,
		IdentityStatus: profilepb.IdentityStatus_PENDING,
	}

	err := s.Mongo.UpdateProfile(c, "account1", profilepb.IdentityStatus_UNSUBMITTED, &p)
	if err != nil {
		s.Logger.Error("can not update profile", zap.Error(err))
		return nil, status.Error(codes.Internal, "")
	}

	return &p, nil
}

func (s Service) ClearProfile(c context.Context, empty *emptypb.Empty) (*profilepb.Proflile, error) {
	p := profilepb.Proflile{}

	err := s.Mongo.UpdateProfile(c, "account1", profilepb.IdentityStatus_VERIFIED, &p)
	if err != nil {
		s.Logger.Error("can not clear profile", zap.Error(err))
		return nil, status.Error(codes.Internal, "")
	}

	return &p, nil
}

func (s Service) GetProfilePhoto(c context.Context, empty *emptypb.Empty) (*profilepb.GetProfilePhotoResponse, error) {
	p, err := s.Mongo.GetProfile(c, "account1")
	if err != nil {
		if err == mongo.ErrNoDocuments {
			return &profilepb.GetProfilePhotoResponse{}, nil
		} else {
			s.Logger.Error("can not get profile", zap.Error(err))
			return nil, status.Error(codes.Internal, "")
		}
	}

	if p.PhotoBlobID == "" {
		return nil, status.Error(codes.NotFound, "")
	}

	br, err := s.BlobClient.GetBlobURL(c, &blobpb.GetBlobURLRequest{
		Id:         p.PhotoBlobID,
		TimeoutSec: int32(s.PhotoExpire.Seconds()),
	})
	if err != nil {
		s.Logger.Error("can not get blob url", zap.Error(err))
		return nil, status.Error(codes.Internal, "")
	}
	return &profilepb.GetProfilePhotoResponse{
		UploadUrl: br.Url,
	}, nil
}

func (s Service) CreateProfilePhoto(c context.Context, empty *emptypb.Empty) (*profilepb.CreateProfilePhotoResponse, error) {
	br, err := s.BlobClient.CreateBlob(c, &blobpb.CreateBlobRequest{
		AccountId:           "account1",
		UploadUrlTimeoutSec: int32(s.PhotoExpire.Seconds()),
	})
	if err != nil {
		s.Logger.Error("can not create", zap.Error(err))
		return nil, status.Error(codes.Aborted, "")
	}

	err = s.Mongo.UpdateProfilePhoto(c, "account1", br.Id)
	if err != nil {
		s.Logger.Error("can update profile photo", zap.Error(err))
		return nil, status.Error(codes.Aborted, "")
	}

	return &profilepb.CreateProfilePhotoResponse{
		UploadUrl: br.UploadUrl,
	}, nil

}

func (s Service) CompleteProfilePhoto(c context.Context, empty *emptypb.Empty) (*profilepb.Identity, error) {
	p, err := s.Mongo.GetProfile(c, "account1")
	if err != nil {
		if err == mongo.ErrNoDocuments {
			return &profilepb.Identity{}, nil
		} else {
			s.Logger.Error("can not get profile", zap.Error(err))
			return nil, status.Error(codes.Aborted, "")
		}
	}

	if p.PhotoBlobID == "" {
		return nil, status.Error(codes.NotFound, "")
	}

	br, err := s.BlobClient.GetBlob(c, &blobpb.GetBlobRequest{
		Id: p.PhotoBlobID,
	})
	if err != nil {
		s.Logger.Error("can not get blob", zap.Error(err))
		return nil, status.Error(codes.Aborted, "")
	}

	s.Logger.Info("got profile photo", zap.Int("size", len(br.Data)))
	return &profilepb.Identity{
		LicNumber:          "007007007",
		Name:               "李四",
		Gender:             profilepb.Gender_MALE,
		BirthdayDateMillis: 631152000000,
	}, nil
}

func (s Service) ClearProfilePhoto(c context.Context, in *emptypb.Empty) (*emptypb.Empty, error) {
	err := s.Mongo.UpdateProfilePhoto(c, "account1", "")
	if err != nil {
		s.Logger.Error("can not clear profile photo", zap.Error(err))
		return nil, status.Error(codes.Internal, "")
	}
	return &emptypb.Empty{}, nil
}
